#include "Transform.h"
#include "glm/gtx/matrix_decompose.hpp"
#include "glm/ext.hpp"

Transform::Transform()
{
	mLocalPosition = glm::vec3(0.0f, 0.0f, 0.0f);
	mLocalOrientation = glm::quat(glm::vec3(0.0f, 0.0f, 0.0f));
	mLocalScale = glm::vec3(1.0f, 1.0f, 1.0f);
	mLocalMatrix = glm::mat4(1.0f);
	mWorldMatrix = glm::mat4(1.0f);
	mParentMatrix = glm::mat4(1.0f);
}

Transform::Transform(const glm::mat4& matrix)
{
	glm::vec3 skew;
	glm::vec4 perspective;
	glm::decompose(matrix, mLocalScale, mLocalOrientation, mLocalPosition, skew, perspective);

	mLocalMatrix = matrix;
	mWorldMatrix = matrix;
	mParentMatrix = glm::mat4(1.0f);
}

Transform::Transform(const glm::vec3& position)
{
	mLocalPosition = position;
	mLocalOrientation = glm::quat(glm::vec3(0.0f, 0.0f, 0.0f));
	mLocalScale = glm::vec3(1.0f, 1.0f, 1.0f);
	mLocalMatrix = glm::mat4(1.0f);
	mWorldMatrix = glm::mat4(1.0f);
	mParentMatrix = glm::mat4(1.0f);
	SetLocalPosition(position);
}

Transform::~Transform() = default;

void Transform::UpdateMatrices()
{
	mLocalMatrix = glm::translate(glm::mat4(1.0), mLocalPosition) *glm::toMat4(mLocalOrientation)* glm::scale(glm::mat4(1.0), mLocalScale);

	mWorldMatrix = mParentMatrix * mLocalMatrix;
	mDirty = false;
	mHasUpdated = true;
}

void Transform::ApplyTransform()
{
	glm::vec3 skew;
	glm::vec4 perspective;
	glm::decompose(mLocalMatrix, mLocalScale, mLocalOrientation, mLocalPosition, skew, perspective);
	mDirty = false;
	mHasUpdated = true;
}

void Transform::SetWorldMatrix(const glm::mat4 & mat)
{
	if (mDirty)
		UpdateMatrices();
	mParentMatrix = mat;
	mWorldMatrix = mParentMatrix * mLocalMatrix;
}

void Transform::SetLocalTransform(const glm::mat4 & localMat)
{
	mLocalMatrix = localMat;
	mHasUpdated = true;

	ApplyTransform();

	mWorldMatrix = mParentMatrix * mLocalMatrix;
}

void Transform::SetLocalPosition(const glm::vec3 & localPos)
{
	mDirty = true;
	mLocalPosition = localPos;
}

void Transform::SetLocalScale(const glm::vec3 & newScale)
{
	mDirty = true;
	mLocalScale = newScale;
}

void Transform::SetLocalOrientation(const glm::quat & quat)
{
	mDirty = true;
	mLocalOrientation = quat;
}

const glm::mat4& Transform::GetWorldMatrix()
{
	if (mDirty)
		UpdateMatrices();

	return mWorldMatrix;
}

const glm::mat4& Transform::GetLocalMatrix()
{
	if (mDirty)
		UpdateMatrices();

	return mLocalMatrix;
}

const glm::vec3 Transform::GetWorldPosition() const
{
	return mWorldMatrix[3];
}

const glm::quat Transform::GetWorldOrientation() const
{
	return glm::toQuat(mWorldMatrix);
}

const glm::vec3& Transform::GetLocalPosition() const
{
	return mLocalPosition;
}

const glm::vec3& Transform::GetLocalScale() const
{
	return mLocalScale;
}

const glm::quat& Transform::GetLocalOrientation() const
{
	return mLocalOrientation;
}
